package com.fly.show.tls;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fly.core.utils.ShellExecutor;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Api(tags = "TLS")
@CrossOrigin
@RestController
@RequestMapping("/tls")
public class TLSController
{
    AtomicInteger count = new AtomicInteger(0);
    
    Resource readMe = new ClassPathResource("docker-tls.md");
    
    @ApiOperation("创建证书并下载")
    @ApiOperationSupport(order = 10)
    @ApiImplicitParam(name = "ip", value = "服务器IP", required = true)
    @PostMapping(value = "/create", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    public void create(String ip, HttpServletResponse response)
        throws IOException
    {
        try
        {
            if (!isIp4(ip))
            {
                response.setStatus(HttpStatus.BAD_REQUEST.value()); // 设置状态码
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
                response.getWriter().print("服务器IP非法,请修改");
                return;
            }
            
            // 使用计数器控制并发
            if (count.incrementAndGet() > 1)
            {
                response.setContentType(MediaType.APPLICATION_JSON_VALUE);
                response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
                response.getWriter().print("请求用户过多,请稍后再试!");
                return;
            }
            // 获取需要执行的shell脚本
            ip = StringUtils.trimToEmpty(ip);
            String text = IOUtils.resourceToString("/docker/create_tls_certs.sh", StandardCharsets.UTF_8);
            text = String.format(text, ip);
            
            // 持久化后执行
            FileUtils.writeStringToFile(new File("/data/create_tls_certs.sh"), text, StandardCharsets.UTF_8);
            ShellExecutor.exec("cd /data/ && dos2unix * && sh create_tls_certs.sh");
            response.setHeader("Content-Disposition", "attachment;filename=" + String.format("%s-%s", ip, System.currentTimeMillis() % 1440000) + ".zip");
            try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream()); InputStream inputStream = readMe.getInputStream())
            {
                FileUtils.listFiles(new File("/data/pems/"), new String[] {"pem"}, true).forEach(file -> addToZip(zipOutputStream, file));
                zipOutputStream.putNextEntry(new ZipEntry("ReadMe.md"));
                StreamUtils.copy(inputStream, zipOutputStream);
                zipOutputStream.flush();
            }
        }
        finally
        {
            log.info("---- 计数器释放：{}", count.decrementAndGet());
        }
    }
    
    /**
     * 判断是否合法
     * 
     * @param ip4
     * @return
     * @see [类、类#方法、类#成员]
     */
    private boolean isIp4(String ip4)
    {
        String[] ipArr;
        return StringUtils.isNotBlank(ip4) // 非空
            && StringUtils.countMatches(ip4, ".") == 3 // 3dots
            && (ipArr = StringUtils.split(ip4, ".")).length == 4 // 4位
            && Stream.of(ipArr)
                .filter(it -> StringUtils.equals(it, String.valueOf(NumberUtils.toInt(it))))// 合法数字
                .map(NumberUtils::toInt)
                .filter(it -> (0 <= it && it <= 255))// [0, 255]
                .count() == 4;
    }
    
    /**
     * 添加下载文件
     * 
     * @param zipOutputStream
     * @param file
     * @see [类、类#方法、类#成员]
     */
    private void addToZip(ZipOutputStream zipOutputStream, File file)
    {
        try (InputStream inputStream = Files.newInputStream(file.toPath()))
        {
            zipOutputStream.putNextEntry(new ZipEntry(StringUtils.substringAfter(file.getCanonicalPath(), "/data/pems/")));
            StreamUtils.copy(inputStream, zipOutputStream);
            zipOutputStream.flush();
        }
        catch (IOException e)
        {
            log.error(e.getMessage(), e);
        }
    }
}
